/*
 * @Author: ZhanXie
 * @Date: 2020-02-02 20:17:46
 * @LastEditors: ZhanXie
 * @LastEditTime: 2020-03-16 17:53:53
 * @Description: 对 request 中的 `url` 做一层转换
 * @FilePath: /projectStudy/ts-axios/src/helpers/url.ts
 */
import { isDate, isPlainObject, isURLSearchParams } from './util'

interface URLOrigin {
  protocol: string
  host: string
}

/**
 * 对 url 参数进行编码
 * @param val
 */
function encode(val: string): string {
  return encodeURIComponent(val)
    .replace(/%40/g, '@')
    .replace(/%3A/gi, ':')
    .replace(/%24/g, '$')
    .replace(/%2C/gi, ',')
    .replace(/%20/g, '+')
    .replace(/%5B/gi, '[')
    .replace(/%5D/gi, ']')
}

export function buildURL(
  url: string,
  params?: any,
  paramsSerializer?: (params: any) => string
): string {
  if (!params) {
    // 如果没有参数直接返回 url
    return url
  }

  let serializedParams

  if (paramsSerializer) {
    // 自定义处理
    serializedParams = paramsSerializer(params)
  } else if (isURLSearchParams(params)) {
    // 如果它是一个 `URLSearchParams` 对象实例的话，我们直接返回它 `toString` 后的结果
    serializedParams = params.toString()
  } else {
    // 默认处理
    const parts: string[] = [] // [key=val] 参数 键值对 格式化处理之后放入数组中
    Object.keys(params).forEach(key => {
      const val = params[key]
      if (val === null || typeof val === 'undefined') {
        return
      }
      let values = []
      if (Array.isArray(val)) {
        // 参数为数组 的时候做处理 key[] = val
        values = val
        key += '[]'
      } else {
        // 包装成数组统一处理
        values = [val]
      }
      values.forEach(val => {
        if (isDate(val)) {
          val = val.toISOString()
        } else if (isPlainObject(val)) {
          val = JSON.stringify(val)
        }
        parts.push(`${encode(key)}=${encode(val)}`)
      })
    })

    serializedParams = parts.join('&')
  }

  if (serializedParams) {
    const markIndex = url.indexOf('#')
    if (markIndex !== -1) {
      url = url.slice(0, markIndex)
    }
    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
  }

  return url
}

/**
 * 判断 自定义url 和 当前url 是否是同源请求
 * 同域名的判断主要利用了一个技巧，创建一个 a 标签的 DOM，然后设置 `href` 属性为我们传入的 `url`，然后可以获取该 DOM 的 `protocol`、`host`。
 * 当前页面的 `url` 和请求的 `url` 都通过这种方式获取，然后对比它们的 `protocol` 和 `host` 是否相同即可。
 * @param requestURL
 */
export function isURLSameOrigin(requestURL: string): boolean {
  const parsedOrigin = resolveURL(requestURL)
  return (
    parsedOrigin.protocol === currentOrigin.protocol && parsedOrigin.host === currentOrigin.host
  )
}

const urlParsingNode = document.createElement('a') // 自定义 URLOrigin
const currentOrigin = resolveURL(window.location.href) // 当前 URLOrigin

/**
 * 根据 a 标签的 dom 对象获取 protocol 和 host
 * @param url
 */
function resolveURL(url: string): URLOrigin {
  urlParsingNode.setAttribute('href', url)
  const { protocol, host } = urlParsingNode

  return {
    protocol,
    host
  }
}

/**
 * 是否是绝对路径
 * @param url
 */
export function isAbsoluteURL(url: string): boolean {
  return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
}

/**
 * 合并路径
 * @param baseURL
 * @param relativeURL
 */
export function combineURL(baseURL: string, relativeURL?: string): string {
  return relativeURL ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL
}
